import re
import numpy as np
from semver_range import Version, Range
import semver as sv
import global_var

# 从依赖json文件获得的名字可能由于文本不规范，会带上分号、中括号或者甚至带上版本的情况，在这里只需要包名
def get_dep_name(name):
    pattern = re.compile(r'[\w.-]+')
    res = pattern.match(name)
    if res is None:
        log = open("./dep_log.txt", "a")
        log.write("fail to get dep_name %s\n" % name)
        log.close()
        return None
    else:
        return name[res.start():res.end()]

# get所有可能出现在版本号之前的比较符号
def get_op():
    op_set = set()
    pattern = re.compile(r'[^0-9\'\"\s]+')
    for file_count in range(1, 58):
        pkg_dep = np.load("./pkg_dep/pkg_dep_%s.npy" % file_count, allow_pickle=True).tolist()
        for each in pkg_dep:
            dependencies = each['dependence']
            for dep in dependencies:
                requirements = dep['requirements'].split(',')
                # if(len(requirements) > 1):
                #     print(dep['requirements'])
                for require in requirements:
                    if require == '':
                        continue
                    require = require.strip().strip(';')
                    res = pattern.match(require)
                    op = require[res.start():res.end()]
                    op_set.add(op)
                    # if(op == '>dev'):     # 进行一些单项测试
                    #     print(require)
    for each in op_set:
        print(each)

# 格式化版本格式信息符合semver要求
def normalize_version(version_str):

    version_str = version_str.replace(' ', '')
    version_str = version_str.strip('-')
    version_str = version_str.strip('.')

    # 已经符合semver要求
    if sv.VersionInfo.isvalid(version_str):
        return version_str

    # 例如2.1.*这样的字符串
    if "*" in version_str:
        isRange = True
        try:
            Range(version_str, loose=True)
        except:
            isRange = False
        if(isRange):
            return version_str

    old_str = version_str

    # 不规范1:  1.0 -> 1.0.0 or 1 -> 1.0.0
    pattern = r'(\d+\.\d+\.\d+\D?.*)'
    match = re.search(pattern, version_str)
    if match is None:
        pattern = r'(\d+\.\d+)(\D?.*)'
        match = re.search(pattern, version_str)
        if match:
            norm_ver = version_str[0:match.regs[1][1]] + ".0" + version_str[match.regs[1][1]:]
            if sv.VersionInfo.isvalid(norm_ver):
                return norm_ver
            else:
                version_str = norm_ver
        else:
            pattern = r'(\d+)(\D?.*)'
            match = re.search(pattern, version_str)
            if match:
                norm_ver = version_str[0:match.regs[1][1]] + ".0.0" + match.group(2)
                if sv.VersionInfo.isvalid(norm_ver):
                    return norm_ver
                else:
                    version_str = norm_ver

    # 不规范2:  2.0.0.a -> 2.0.0-a
    pattern = r'(\.)([a-zA-Z])'
    match = re.search(pattern, version_str)
    if match:
        norm_ver = version_str[0:match.regs[1][0]] + "-" + version_str[match.regs[1][1]:]
        if sv.VersionInfo.isvalid(norm_ver):
            return norm_ver
        else:
            version_str = norm_ver

    # 不规范3:  2.0.0a -> 2.0.0-a
    pattern = r'(\.)([0-9]+)([a-zA-Z])'
    match = re.search(pattern, version_str)
    if match:
        norm_ver = version_str[0:match.regs[3][0]] + "-" + version_str[match.regs[3][0]:]
        if sv.VersionInfo.isvalid(norm_ver):
            return norm_ver
        else:
            version_str = norm_ver

    # 不规范4:  0.12.01 -> 0.12.1 or 2015.04.28 -> 2015.4.28
    pattern = r'(\.)(0\d+)(\.)'
    match = re.search(pattern, version_str)
    if match:
        norm_ver = version_str[0:match.regs[2][0]] + version_str[match.regs[2][0] + 1:]
        if sv.VersionInfo.isvalid(norm_ver):
            return norm_ver
        else:
            version_str = norm_ver

    pattern = r'(\.)(0\d+)'
    match = re.search(pattern, version_str)
    if match:
        norm_ver = version_str[0:match.regs[1][1]] + version_str[match.regs[2][0] + 1:]
        if sv.VersionInfo.isvalid(norm_ver):
            return norm_ver
        else:
            version_str = norm_ver

    # 不规范5:  1.0.0.1 -> 1.0.0-1
    pattern = r'(\d+\.\d+\.\d+)(\.\d+\D?.*)'
    match = re.search(pattern, version_str)
    if match:
        norm_ver = version_str[0:match.regs[1][1]] + "-" + version_str[match.regs[1][1] + 1:]
        if sv.VersionInfo.isvalid(norm_ver):
            return norm_ver

    log = open("log.txt", "a")
    log.write("error version string %s\n" % old_str)
    log.close()
    return None

# 格式化比较符号
def normalize_op(op_str):
    op_list = [">", "<", ">=", "<=", "=", "~", "^", "!="]
    if op_str in op_list:
        return op_str
    if op_str == "~=":
        return "~"
    if op_str == "==v":
        return "="
    if op_str == "!=<" or op_str == ">v" or op_str == ">+":
        return ">"
    if op_str == "==":
        return "="
    if op_str == "^=":
        return "^"
    return None

# 格式化依赖版本范围信息符合semver要求
def normalize_range(range_str):
    if '(' in range_str and ')' in range_str:
        s = range_str.index('(')
        e = range_str.index(')')
        range_str = range_str[s + 1:e]
    range_str = range_str.replace(';', '')
    range_str = range_str.replace('\"', '').replace("\'", '')
    ranges = range_str.split(',')

    if len(ranges) == 1:    # 全需要的情况
        range_str = range_str.strip()
        if range_str == "" or range_str == "*" or range_str == ">dev" or re.match(r'[a-zA-Z]', range_str):
            return "*"

    norm_range = ""
    for range in ranges:
        range = range.strip()
        match = re.search(r'(\D*)(\d.*)', range)
        if match is None:
            # print(range)
            return None
        op = normalize_op(range[0:match.regs[1][1]])
        ver = normalize_version(range[match.regs[2][0]:])
        if op is None or ver is None:
            return None
        else:
            norm_range += op + ver + ' '

    norm_range = norm_range.strip()

    return norm_range

# 处理不带有不等号的range字符串
def get_require_versions(range_str, ver_list):
    res = []
    range = Range(range_str, loose=True)
    for ver_str in ver_list:
        norm_str = normalize_version(ver_str)
        if norm_str is None:
            continue
        ver = Version(norm_str, loose=True)
        if ver in range:
            res.append(ver_str)
    return res


# 处理带有不等号的range字符串
def get_require_versions_with_not_equal(range_str, ver_list):
    ranges = range_str.split(' ')   # 切分成单range处理
    ranges1 = []    # 不带有不等号range
    ranges2 = []    # 带有不等号range
    res = []
    for range in ranges:
        if "!=" in range:
            ranges2.append(Range(range.replace("!=", "="), loose=True))
        else:
            ranges1.append(Range(range, loose=True))

    for ver_str in ver_list:
        norm_str = normalize_version(ver_str)
        if norm_str is None:
            continue
        ver = Version(norm_str, loose=True)

        flag = 1
        for range in ranges1:
            if ver not in range:
                flag = 0
                break
        if flag == 0:
            continue

        for range in ranges2:
            if ver in range:
                flag = 0
                break
        if flag == 0:
            continue
        res.append(ver_str)     # 添加原版本而不是标准化后的版本

    return res

def get_dep_relations(start, end):

    all_dict = global_var.load_ver_dict()
    print("strart analyse dep from file %s to file %s" % (start, end-1))
    for file_count in range(start, end):
        pkg_dep = np.load("./pkg_dep/pkg_dep_%s.npy" % file_count, allow_pickle=True).tolist()
        count = 1
        for each in pkg_dep:
            id = each['id']
            dependencies = each['dependence']
            for dep in dependencies:
                dep_name = get_dep_name(dep['name'])
                requirements = dep['requirements']
                ver_list = all_dict.get(dep_name)
                if ver_list is None:
                    continue
                ver_satisfy = []
                range_str = normalize_range(requirements)
                if range_str is None:
                    continue
                if "!=" in range_str:
                    ver_satisfy.extend(get_require_versions_with_not_equal(range_str, ver_list))
                else:
                    ver_satisfy.extend(get_require_versions(range_str, ver_list))

                # get all
                # for ver in ver_satisfy:
                #     global_var.pkg_dep_pkg.append({'pkg_a': id, 'pkg_b': dep_name + "@" + ver})

                # get max statisfy
                if len(ver_satisfy) > 0:
                    global_var.pkg_dep_pkg.append({'pkg_a': id, 'pkg_b': dep_name + "@" + ver_satisfy[-1]})

            if count % 1000 == 0:
                print("finish %s:%s relations:%s" % (file_count, count, len(global_var.pkg_dep_pkg)))

            count += 1

        print("colllect %s relations" % len(global_var.pkg_dep_pkg))
        np.save("./pkg_dep_pkg_max/pkg_dep_pkg_max_%s.npy" % file_count, global_var.pkg_dep_pkg)
        global_var.pkg_dep_pkg.clear()



# get_op()
# ver_list = ["1.9.1-a", "2.0.3-a", "3.1.1.1", "3.5.2", "2.1.2"]
# ver_range = Range("1")
# print(Version('1.4.2', loose=True) in ver_range)
# print(sv.VersionInfo.parse('2.2.2').match('!=1.9.*'))
# range = normalize_range('>"2.0.3",<=3.2,!=1.9.1')
# print(get_require_versions_with_not_equal(range, ver_list))
# get_dep_relations()


